相机控制 PRO
通过 iPhone 相机控制键打开 Scripting 相机体验。系统直接打开主 App 时,会运行所选脚本的 index.tsx,注入 Script.env === "camera_control",由脚本绘制完整 UI。此环境与 index、home_screen 的 API 范围一致,仍分别遵守系统版本、硬件、前台状态和脚本/系统权限要求。
相机控制入口应进入真实拍摄体验:拍摄时显示可见取景,并在页面上注册可用的 onCameraCaptureEvent。处理后续 Script.onResume 事件,返回拍摄体验时恢复界面;相机参数为 null 只表示没有新资源批次。避免停留在相机已停止的空页面,也不要重复处理旧批次。完整示例通过 Close 结束拍摄并释放资源。仅接收文件不会自动启动相机;用户点 Start Camera 或再次从相机入口进入时才恢复拍摄。
系统拍摄入口要求
**系统打开主 App 时:**相机控制键和系统中的 Scripting 相机 控件都是拍摄入口。自定义脚本需要自行启动真实相机会话、显示预览并处理拍摄事件,脚本约 3 秒后仍未启动相机并提供可用拍摄交互时,Scripting 会尝试打开内置相机;脚本已打开系统相机页面时则继续使用该页面。这项保护只覆盖启动阶段,相机就绪后不会持续监控。之后停止相机却仍停留在拍摄入口,仍可能导致整个 App 被系统终止;JS 错误处理不能捕获系统对进程的终止。Apple 将该 intent 定义为拍摄体验入口,也有开发者在旧版 iOS 上记录过主 App 被终止的情况。
保持活动取景不代表必须持续录像或保存照片、视频。
内置相机先保存拍摄完成的照片/视频,再通过 Script.cameraControlParameter 或 Script.onResume 交给原脚本。原脚本无法接收,或默认选择已变化时,批次会保留在“更多 → 默认脚本 → 相机控制 → 拍摄文件”,供手动处理。接管前会等待脚本释放相机。共享会话无法安全停止、权限拒绝或主线程无法响应时,兜底也可能无法完成;约 3 秒的等待不是系统保证。
系统入口未选择默认相机脚本时,先显示内置相机,再在其上打开脚本选择页。选择后会提示下次打开 Scripting 相机时生效;取消选择则仍留在相机。本次拍摄文件会保留供手动处理,不会自动交给刚选择的脚本。
**系统打开锁屏拍摄扩展时:**Apple 明确要求活动相机视图、硬件拍摄交互以及相机访问请求,否则扩展可能在启动后不久被终止。该原生相机由 Scripting 提供,详见 Apple 拍摄扩展指南。
**停止与清理:**上述文档没有定义主 App 的持续检查时序,也没有说明每次停止相机都会终止 App。不要依赖固定宽限时间或短暂开关相机的变通办法。退出时仍应 stop/dispose 资源;完成清理不代表已保证解除系统的拍摄入口检查。
仅接收已完成的文件不要求另开相机;若同一脚本也被选为系统相机目标,还应处理系统直接打开主 App 进行拍摄的情况。Script.env === "camera_control" 和相机参数为 null 都不能证明具体启动来源。App 内的“打开相机脚本”只测试脚本,不验证系统拍摄入口的行为。与拍摄无关的通用脚本应使用普通 App、Home 或快捷指令入口。参见 Apple 启动指引。
设置
- 从内置示例安装 相机控制示例(Camera Control DEMO)。
- 打开 更多 → 默认脚本 → 相机控制,选择脚本。点“打开相机脚本”即可在没有相机键的设备上体验脚本页面。
- 在支持的 iPhone 上,通过 设置 → 相机 → 相机控制 选择 Scripting。该系统选项由用户设置。
- 也可将 Scripting 相机 控件添加到控制中心或锁定屏幕。
在 iOS 26+ 上打开 Scripting 相机时,会优先使用已有的主 App 窗口。相机提示和脚本选择显示在本次拍摄所在窗口,关闭内置相机后返回该窗口。
从锁屏入口进入时,Scripting 提供原生拍照、录像界面。拍摄保存完成后点“让 Scripting 处理”,系统按需认证,打开主 App 并把文件交给所选脚本。实际打开主 App 还是扩展由系统决定;即使已通过认证但仍停留在锁屏界面,也可能进入扩展。
扩展不执行用户脚本。脚本收到的是完成的文件,不是扩展进程中的实时相机会话。普通运行或无拍摄资源的主 App 相机入口中,Script.cameraControlParameter === null;扩展资源交付或内置兜底相机完成拍摄后则为非空对象。完整约定见拍摄资源。
锁屏取景会在拍摄页面出现时准备;启动中会显示提示。相机不可用或被中断时会显示原因,可在恢复可用后继续,或点“重试相机”。录像必须先停止并保存完成,才能交给脚本。取消解锁或打开 App 失败时,已完成的文件仍保留;返回拍摄页面后可继续拍摄或再次点“让 Scripting 处理”。
首次锁屏交接前请先选择默认脚本。在相机控制示例中,点击 Captured files 下的照片或视频即可预览。如果先拍摄、后选择脚本,可到“更多 → 默认脚本 → 相机控制 → 拍摄文件”对已保存批次点“让 Scripting 处理”;仅选择脚本不会自动运行旧批次。
在锁屏录像模式中,点击模式选择旁的麦克风按钮切换是否录制声音;麦克风带斜线时录制无声视频。
自定义脚本相机
在主 App 中使用 AVCaptureSession、AVCaptureDeviceInput、AVCapturePhotoOutput、AVCaptureMovieFileOutput 与 CaptureVideoPreviewView。给相机页面添加 onCameraCaptureEvent 处理实体快门事件,同时保留调用相同动作的触控按钮。系统缩放等控件仍使用 supportsControls、canAddControl、setControlsDelegate。secondary 不代表半按;不要在同一页面再附加一个 AVCaptureEventInteraction。
同级 index.tsx 示例可以预览扩展交付的照片/视频,由用户选择保存到照片库,并在没有资源时提供简单的脚本拍照界面。只有启动相机才请求相机权限;查看已有资源不会启动相机。系统拍摄入口与新增修饰符需要 iOS 18+。Simulator 可体验页面与数据流,但无法复现实体相机键事件。
请使用最新的 相机控制示例(Camera Control DEMO 1.0.2)。示例的 Close 会先等待相机停止,再关闭页面;已安装旧版示例时,请参考此完整示例更新其 index.tsx。
页面关闭时停止并 dispose 相机会话和播放器。等待权限时停止会取消该次启动,不会在脚本退出后迟到启动。保存到照片库沿用 Photos 权限;示例不会自动上传或自动保存到照片库。
请在脚本中处理相机生命周期错误。权限被拒绝、配置事务未结束、启动失败或本次启动已取消时,startRunning() 会拒绝。请使用同步的 configure(() => { ... }),或者在启动前为每个 beginConfiguration() 配对调用 commitConfiguration()。停止自建会话会结束其未完成的配置,并取消尚未完成的启动;之后可显式再次启动重试。dispose() 是最终释放,再次拍摄需要新建会话。
执行相机脚本或同步恢复回调时报告未捕获错误,该脚本会结束并释放拍摄资源,不再继续调用该失败脚本后续的恢复回调。请用 try/catch 或 .catch() 处理异步任务的错误;恢复流程不会等待恢复回调返回的 Promise。脚本退出后不再交付拍摄控件和拍摄交互回调;脚本仍在运行时,已分离的交互可以重新挂载。仅接收文件的入口或文件恢复回调可以处理批次后正常调用 Script.exit(),这会完成交付,不需要保持取景;未捕获错误仍算交付失败。这些保护不能替代前述系统拍摄入口要求。
完整 index.tsx 示例
点击文档的示例入口即可运行,完整代码见同级 index.tsx。
